#include "stdafx.h"
#include "EventHandlers.h"

#include <map>

#include <HSModel/Model3D.h>

#include <HSModel/HeightMap.h>
#include <HSModel/GenerateSurface.h>
#include <HSModel/SharedConstants.h>
#include <HSModel/FindWaterAreas.h>
#include <HSModel/CreateHill.h>
#include <HSModel/Ground.h>
#include <HSModel/Picker.h>
#include <HSModel/Water.h>

#include <HSModel/Model3DStorage.h>

#include <HSModel/Validator.h>
#include <HSModel/Highlighting.h>

#include "Model3DRenderer.h"

#include <HSModel/TransformCoords.h>
#include <HSModel/HeightMapCoordinates.h>
#include <HSModel/TextureUtils.h>
#include <HSModel/Model3DUtils.h>
#include <HSModel/Highlighting.h>

namespace
  {
  TRay3 GetRayForPicker(const Ogre::Viewport& i_viewport, TVector2 i_mouse_click_coordinates)
    {
    Ogre::Ray ray;
    i_viewport.getCamera()->getCameraToViewportRay(
      i_mouse_click_coordinates.X() / Ogre::Real(i_viewport.getActualWidth()),
      i_mouse_click_coordinates.Y() / Ogre::Real(i_viewport.getActualHeight()), &ray);

    TVector3 origin    = Coords::UIToWorld(TVector3(ray.getOrigin().x, ray.getOrigin().y, ray.getOrigin().z));
    TVector3 direction = Coords::UIToWorld(TVector3(ray.getDirection().x, ray.getDirection().y, ray.getDirection().z));

    return TRay3(origin, direction);
    }

  bool GetPickedPointInWorldCoords(TVector3& o_point, const Ogre::Viewport& i_viewport, TVector2 i_mouse_click_coordinates, Picker& i_picker)
    {
    /* std::cout << "Origin: " << origin.X() << "  " << origin.Y() << "  " << origin.Z() << std::endl;
    std::cout << "Direction:  " << direction.X() << " " << direction.Y() << " " << direction.Z() << std::endl;*/
    auto result = i_picker.GetIntersectionPoint(o_point, GetRayForPicker(i_viewport, i_mouse_click_coordinates));
    /*std::cout << "Result: " << result << "  " << io_point.X() << " " << io_point.Y() << " " << io_point.Z() << std::endl;*/
    return result;
    }

  bool GetPickedTriangleInWorldCoords(TTriangle3& o_triangle, const Ogre::Viewport& i_viewport, TVector2 i_mouse_click_coordinates, Picker& i_picker)
    {
    //std::cout << "Origin: " << origin.X() << "  " << origin.Y() << "  " << origin.Z() << std::endl;
    //std::cout << "Direction:  " << direction.X() << " " << direction.Y() << " " << direction.Z() << std::endl;
    auto result = i_picker.GetIntersectionTriangle(o_triangle, GetRayForPicker(i_viewport, i_mouse_click_coordinates));
    //std::cout << "Result: " << result << "  triangle[0] : " << io_triangle.V[0].X() << " " << io_triangle.V[0].Y() << " " << io_triangle.V[0].Z() << std::endl;
    return result;
    }

  bool GetPickedObjectId(size_t& o_id, const Ogre::Viewport& i_viewport, TVector2 i_mouse_click_coordinates, Picker& i_picker)
    {
    //std::cout << "Origin: " << origin.X() << "  " << origin.Y() << "  " << origin.Z() << std::endl;
    //std::cout << "Direction:  " << direction.X() << " " << direction.Y() << " " << direction.Z() << std::endl;
    bool has_intersection = i_picker.GetIntersectionModelId(o_id, GetRayForPicker(i_viewport, i_mouse_click_coordinates));
    return has_intersection;
    }

 
  std::shared_ptr<Model3D> GetHighlightedModel(const Application& i_application, TVector2 i_mouse_click_coordinates)
    {
    TTriangle3 triangle1;
    bool result1 = GetPickedTriangleInWorldCoords(triangle1, *(i_application.GetOgre().GetViewport()), i_mouse_click_coordinates, i_application.GetPicker());

    std::shared_ptr<Model3D> p_model = nullptr;
    
    if(!result1)
      return nullptr;

    p_model = Highlighting::GetHighlightedModel(triangle1, *i_application.GetWorld().GetGround());

    return p_model;
    }

  bool GetObjectPlacementCoords(TVector3 &o_coords, const Application &i_application, TVector2 i_mouse_coords)
    {
    TVector3 world_point;
    auto result = GetPickedPointInWorldCoords(world_point,
      *(i_application.GetOgre().GetViewport()), i_mouse_coords,
      i_application.GetPicker());

    if(!result)
      return false;
    
    HeightMapUtils::GetObjectPlacementCoords(o_coords, *i_application.GetWorld().GetGround(), world_point);

    return true;
    }

  void ProcessAddObjectHighlighting(std::shared_ptr<Model3DRenderer> &op_model_renderer,
                                    std::shared_ptr<Model3DRenderer> &op_cell_renderer,
                                    Application& m_application, TVector2 i_mouse_coords,
                                    const std::string& i_model_name,
                                    EWorldEntityTypes i_entity_type)
    {
    TVector3 coords;
    const double height_offset = 0.1;

    bool result = GetObjectPlacementCoords(coords, m_application, i_mouse_coords);
    if(!result)
      {
      op_cell_renderer.reset();
      op_model_renderer.reset();
      return;
      }

    std::shared_ptr<Model3D> p_cell_model;
    if(i_entity_type == WE_FERRIS) // It is big so we fallback to simple big plane generation
      p_cell_model = Highlighting::GetCellsHighlighting(m_application.GetWorld().GetGround()->GetHeightMap(), coords, 1);
    else
      p_cell_model = GetHighlightedModel(m_application, i_mouse_coords);

    if (p_cell_model == nullptr)
      return;

    Validator validator;
    if(validator.CanObjectBePlacedHere(coords, m_application.GetWorld(), m_application.GetGameController(),
                                        i_entity_type))
      {
      p_cell_model -> SetMaterialName("Highlighting/Grass");

      // Render model
      op_model_renderer.reset();
      op_model_renderer.reset(new Model3DRenderer(m_application.GetModelStorage().GetModel(i_model_name),
        m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"),
        m_application.GetOgre().GetRoot()->getMeshManager(), "HIGHLIGHT_MODEL_NAME", "HIGHLIGHT_MODEL",
        Coords::WorldToUI(coords), true));
      }
    else
      {
      p_cell_model -> SetMaterialName("Highlighting/Forbidden");

      // Do not render model
      op_model_renderer.reset();
      }      

    std::string base_type = "HIGHLIGHTED_AREA";
    op_cell_renderer.reset();  // Should delete old renderer first
    op_cell_renderer.reset(new Model3DRenderer(*p_cell_model.get(),
      m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"),
      m_application.GetOgre().GetRoot()->getMeshManager(), base_type + "_NAME",
      base_type, TVector3(0, height_offset ,0), true));

    }
  }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

EventHandler::EventHandler(const std::string &i_name)
  :m_name(i_name)
  {
  }

std::string EventHandler::GetName() const
  {
  return m_name;
  }

EventHandler::~EventHandler()
  {

  };

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CreateHillEventHandler::CreateHillEventHandler(Application& i_application)
  : m_application(i_application), EventHandler("CreateHill")
  {
  is_valid = true;
  }

void  CreateHillEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  _ClickAction(evt, id);
  _MoveAction(evt);
  }
void CreateHillEventHandler::MouseMove(const OIS::MouseEvent &evt) 
  {
  _MoveAction(evt);
  }

void CreateHillEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }

void CreateHillEventHandler::_ClickAction(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if (id == OIS::MB_Left)
    {
    TVector3 world_point;
    auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result && is_valid)
      {
      m_application.GetWorldUpdater().CreateHill(TVector2(world_point.X(), world_point.Y()));
      }
    }
  }
void CreateHillEventHandler::_MoveAction(const OIS::MouseEvent &evt)
  {
  mp_highlighted_model.reset(new Model3D());
  TVector3 world_point;
  auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
  if(result)
    {
    HeightMapUtils::GetTrianglesThatWillBeChanged(*mp_highlighted_model,
      m_application.GetWorld().GetGround()->GetHeightMap(),
      TVector2(world_point.X(), world_point.Y()),
      GroundConstants::HMAP_CHANGE,
      D_UP);  

    Validator validator;
    if(!validator.CanChangeTerrain(*mp_highlighted_model, *m_application.GetWorld().GetWorldInfoController()))
      {
      m_render_triangles.reset();
      is_valid = false;
      return;
      }
    else
      {
      is_valid = true;
      }

    std::string base_type = "HIGHLIGHTED_AREA_HILL";
    Textures::ApplyTextures(*mp_highlighted_model);
    mp_highlighted_model->SetMaterialName("Highlighting/HillHole");
    m_render_triangles.reset();  // Should delete old renderer first
    m_render_triangles.reset(new Model3DRenderer(*mp_highlighted_model, m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"), m_application.GetOgre().GetRoot()->getMeshManager(),
      base_type + "_NAME", base_type, TVector3(0,0,0), true));
    }
  }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CreateHoleEventHandler::CreateHoleEventHandler(Application& i_application)
  : m_application(i_application), EventHandler("CreateHole")
  {

  }

void CreateHoleEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  _ClickAction(evt, id);
  _MoveAction(evt);
  }
void CreateHoleEventHandler::MouseMove(const OIS::MouseEvent &evt) 
  {
  _MoveAction(evt);
  }

void CreateHoleEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }

void CreateHoleEventHandler::_ClickAction(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if (id == OIS::MB_Left)
    {
    TVector3 world_point;
    auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result && is_valid)
      {
      m_application.GetWorldUpdater().CreateHole(TVector2(world_point.X(), world_point.Y()));
      }
    }
  }
void CreateHoleEventHandler::_MoveAction(const OIS::MouseEvent &evt)
  {
  mp_highlighted_model.reset(new Model3D());
  TVector3 world_point;
  auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
  if(result)
    {
    HeightMapUtils::GetTrianglesThatWillBeChanged(*mp_highlighted_model,
      m_application.GetWorld().GetGround()->GetHeightMap(),
      TVector2(world_point.X(), world_point.Y()),
      GroundConstants::HMAP_CHANGE,
      D_DOWN);
    
    Validator validator;
    if(!validator.CanChangeTerrain(*mp_highlighted_model, *m_application.GetWorld().GetWorldInfoController()))
      {
      m_render_triangles.reset();
      is_valid = false;
      return;
      }
    else
      {
      is_valid = true;
      }

    std::string base_type = "HIGHLIGHTED_AREA_HOLE";
    Textures::ApplyTextures(*mp_highlighted_model);
    mp_highlighted_model->SetMaterialName("Highlighting/HillHole");
    m_render_triangles.reset(); // Should delete old renderer first
    m_render_triangles.reset(new Model3DRenderer(*mp_highlighted_model, m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"), m_application.GetOgre().GetRoot()->getMeshManager(),
      base_type + "_NAME", base_type, TVector3(0,0,0), true));
    }
  }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AddTreeEventHandler::AddTreeEventHandler(Application& i_application)
  : m_application(i_application), EventHandler("AddTree")
  { 
  }

void  AddTreeEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  if (id == OIS::MB_Left)
    {
    TVector3 world_point;
    auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result)
      {
      m_application.GetWorldUpdater().AddTree(world_point);
      }
    }
  }

void AddTreeEventHandler::MouseMove(const OIS::MouseEvent &evt)
  {
  TVector2 mouse_coords(evt.state.X.abs, evt.state.Y.abs);

  ProcessAddObjectHighlighting(mp_model_renderer, mp_cell_renderer, m_application,
                                mouse_coords, "DEFAULT_TREE_0", WE_TREE);
  }

void AddTreeEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AddFerrisWheelEventHandler::AddFerrisWheelEventHandler(Application& i_application)
  : m_application(i_application), EventHandler("AddFerrisWheel")
  { 
  }

void  AddFerrisWheelEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  if (id == OIS::MB_Left)
    {
    TVector3 point;
    bool result = GetPickedPointInWorldCoords(point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result)
      {
      m_application.GetWorldUpdater().AddFerrisWheel(point);
      }
    }
  }

void AddFerrisWheelEventHandler::MouseMove(const OIS::MouseEvent &evt)
  {
  TVector2 mouse_coords(evt.state.X.abs, evt.state.Y.abs);

  ProcessAddObjectHighlighting(mp_model_renderer, mp_cell_renderer, m_application,
                               mouse_coords, "DEFAULT_FERRIS_WHEEL_0", WE_FERRIS);
  }

void AddFerrisWheelEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AddBenchEventHandler::AddBenchEventHandler(Application& i_application)
  : m_application(i_application), EventHandler("AddBench")
  {

  }

void AddBenchEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if(OIS::MB_Left == id)
    {
    TVector3 point;
    bool result = GetPickedPointInWorldCoords(point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if(result)
      m_application.GetWorldUpdater().AddBench(TVector2(point.X(), point.Y()));
    }
  }

void AddBenchEventHandler::MouseMove(const OIS::MouseEvent &evt)
  {
  TVector2 mouse_coords(evt.state.X.abs, evt.state.Y.abs);
  ProcessAddObjectHighlighting(mp_model_renderer, mp_cell_renderer, m_application,
                               mouse_coords, "DEFAULT_BENCH_0", WE_BENCH);
  }

void AddBenchEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AddBalloonsShopEventHandler::AddBalloonsShopEventHandler(Application& i_application)
  : m_application(i_application), EventHandler("AddBalloonsShop")
  { 
  }

void  AddBalloonsShopEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  if (id == OIS::MB_Left)
    {
    TVector3 point;
    bool result = GetPickedPointInWorldCoords(point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result)
      {
      m_application.GetWorldUpdater().AddBalloonsShop(point);
      }
    }
  }

void AddBalloonsShopEventHandler::MouseMove(const OIS::MouseEvent &evt)
  {
  TVector2 mouse_coords(evt.state.X.abs, evt.state.Y.abs);

  ProcessAddObjectHighlighting(mp_model_renderer, mp_cell_renderer, m_application,
                               mouse_coords, "DEFAULT_BALLOONS_SHOP_0", WE_BALLOONS_SHOP);
  }

void AddBalloonsShopEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


AddWalkwayEventHandler::AddWalkwayEventHandler(Application& i_application)
  : m_application(i_application), m_is_mouse_button_pressed(false), EventHandler("AddWalkway")
  {  

  }

void AddWalkwayEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  if (id == OIS::MB_Left)
    {
    m_is_mouse_button_pressed = true;
    TVector3 world_point;
    auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result)
      {
      m_application.GetWorldUpdater().CreateWalkway(TVector2(world_point.X(), world_point.Y()));
      }
    }
  }

void AddWalkwayEventHandler::MouseMove(const OIS::MouseEvent &evt)
  {
  const double height_offset = 0.1;

  TVector3 world_point;
  auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
  Validator validator;
	bool is_valid = true;
  if (!result || !validator.CanObjectBePlacedHere(world_point, m_application.GetWorld(), m_application.GetGameController(), EWorldEntityTypes::WE_WALKWAY))
    {
    m_render_triangles.reset();
		is_valid = false;
    }

  mp_highlighted_model = GetHighlightedModel(m_application, TVector2(evt.state.X.abs, evt.state.Y.abs));
  if (mp_highlighted_model == nullptr)
    return;

  if (is_valid)
  mp_highlighted_model->SetMaterialName("Highlighting/Walkway");
	else 
		mp_highlighted_model->SetMaterialName("Highlighting/Forbidden");
  std::string base_type = "HIGHLIGHTED_AREA_WALKWAY";
  m_render_triangles.reset();  // Should delete old renderer first
  m_render_triangles.reset(new Model3DRenderer(*mp_highlighted_model.get(), m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"),
  m_application.GetOgre().GetRoot()->getMeshManager(), base_type + "_NAME", base_type, TVector3(0,height_offset,0), true));

  if(m_is_mouse_button_pressed)
    MouseClick(evt, OIS::MB_Left);
  }

void AddWalkwayEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if(id ==  OIS::MB_Left)
    m_is_mouse_button_pressed = false;
  }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AddGrassEventHandler::AddGrassEventHandler(Application& i_application)
  : m_application(i_application), m_is_mouse_button_pressed(false), EventHandler("AddGrass")
  {  

  }

void AddGrassEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  { 
  if (id == OIS::MB_Left)
    {
    m_is_mouse_button_pressed = true;
    TVector3 world_point;
    auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (result)
      {
      m_application.GetWorldUpdater().AddGrass(TVector2(world_point.X(), world_point.Y()));
      }
    }
  }

void AddGrassEventHandler::MouseMove(const OIS::MouseEvent &evt)
  {
  const double height_offset = 0.1;

  TVector3 world_point;
  Validator validator;
  auto result = GetPickedPointInWorldCoords(world_point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
  bool is_valid = true;
  if(result && !validator.CanObjectBePlacedHere(world_point, m_application.GetWorld(), m_application.GetGameController(), WE_GRASS))
    {
    m_render_triangles.reset();
    is_valid = false;
    }

  mp_highlighted_model = GetHighlightedModel(m_application, TVector2(evt.state.X.abs, evt.state.Y.abs));
  if (mp_highlighted_model == nullptr)
    return;
	if (is_valid)
  mp_highlighted_model -> SetMaterialName("Highlighting/Grass");
	else 
		mp_highlighted_model->SetMaterialName("Highlighting/Forbidden");

  std::string base_type = "HIGHLIGHTED_AREA_GRASS";
  m_render_triangles.reset();  // Should delete old renderer first
  m_render_triangles.reset(new Model3DRenderer(*mp_highlighted_model.get(), m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"),
    m_application.GetOgre().GetRoot()->getMeshManager(), base_type + "_NAME", base_type, TVector3(0,height_offset,0), true));

  if(m_is_mouse_button_pressed)
    MouseClick(evt, OIS::MB_Left);
  }

void AddGrassEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if(id ==  OIS::MB_Left)
    m_is_mouse_button_pressed = false;
  }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CreateWaterEventHandler::CreateWaterEventHandler(Application& i_application)
  :m_application(i_application), EventHandler("CreateWater"), m_picker_highlighted(), m_picker_water()
  {
  const double height_diff = 0.1;
  const std::string base_type = "HIGHLIGHTED_AREA_WATER";
  m_highlighted_index.reset();  

  // get models for possible water to highlight
  m_models = HeightMapUtils::GetWaterModels(i_application.GetWorld().GetGround()->GetHeightMap(),
                                              GroundConstants::WATER_LEVEL);
  
  // check if we can spill the water here
  Validator water_placer_validator;
  
  // creating lambda-expression to estimate if we can create a water part
  auto cannot_place_water_part = [&water_placer_validator, &i_application](std::shared_ptr<Water>& i_water_part)
    {
    if( water_placer_validator.CanPlaceWater(i_water_part->GetModel(), *i_application.GetWorld().GetWorldInfoController()))
      return false;
    else
      return true;
    };
  
  // erasing water parts that can not be created
  m_models.erase(std::remove_if(m_models.begin(), m_models.end(), cannot_place_water_part), m_models.end());
  
  for (auto& it : m_models)
    it->GetModel().SetMaterialName("Highlighting/Blue");

  size_t id = 0;
  for(auto model : m_models)
    {
    m_renderers.push_back(std::shared_ptr<Model3DRenderer>(new Model3DRenderer(model->GetModel(),
      m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"),
      m_application.GetOgre().GetRoot()->getMeshManager(),
      base_type + "_NAME " + std::to_string(id), base_type + std::to_string(id),
      TVector3(0,-height_diff,0), true )));
    id++;
    }

  _InitPicker();
  }

void CreateWaterEventHandler::MouseClick(const OIS::MouseEvent &evt, OIS::MouseButtonID id) 
  {
  TTriangle3 triangle;

  size_t water_index = 0;
  bool picked_water = GetPickedObjectId(water_index, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_picker_water);
  if (picked_water)
    return;

  size_t index = 0;
  bool result = GetPickedObjectId(index, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_picker_highlighted);

  if(!result)
    return;

  m_application.GetWorldUpdater().CreateWater(m_models[index]);
  m_picker_water.AddObject(&m_models[index]->GetModel(), TVector3(0.0, 0.0, 0.0));
  }

void CreateWaterEventHandler::MouseMove(const OIS::MouseEvent &evt) 
  {
  const std::string base_type = "HIGHLIGHTED_AREA_POINTED_WATER";
  const double height_diff = 0.05;
  size_t index = 0;
  
  // check if pointing to water
  bool result = GetPickedObjectId(index, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_picker_highlighted);
  if(!result)
    {
    // reset previously highlighted model
    if (m_highlighted_index.is_initialized())
      {
      m_highlighted_index.reset();
      mp_highlighted_model.reset();
      mp_highlighting_renderer.reset();
      }
    return;
    }

  // do nothing if need to highlight the same model 
  if (m_highlighted_index.is_initialized() && index == m_highlighted_index)
    return;
  else
    {
    // reset highlighted model
    m_highlighted_index.reset(index);
    if (m_highlighted_index.is_initialized())
      {
      mp_highlighting_renderer.reset();
      mp_highlighted_model = m_models[m_highlighted_index.get()]->GetModel().Clone();
      mp_highlighted_model -> SetMaterialName("Highlighting/Water");
      Textures::ApplyTextures(*mp_highlighted_model.get());
      mp_highlighting_renderer.reset(new Model3DRenderer(*mp_highlighted_model.get(), m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"), m_application.GetOgre().GetRoot()->getMeshManager(), base_type, base_type, TVector3(0, height_diff, 0), true));
      }
    }  
  }

void CreateWaterEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }

void CreateWaterEventHandler::_InitPicker()
  {
  for (auto it : m_models)
    m_picker_highlighted.AddObject(&it->GetModel(), TVector3(0,0,0));

  auto water_models = m_application.GetWorld().GetGround()->GetWaterModels();
  for (size_t i = 0; i < water_models.size(); ++i)
    m_picker_water.AddObject(&water_models[i]->GetModel(), TVector3(0, 0, 0));

  }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

DestroyObjectsEventHandler::DestroyObjectsEventHandler(Application& i_app)
  : m_application(i_app), EventHandler("DestroyObjects"), m_picker()
  {
  _InitializePicker();  
  }

void DestroyObjectsEventHandler::MouseClick(const OIS::MouseEvent& evt, OIS::MouseButtonID id)
  {
  if (id == OIS::MB_Left)
    {
    // check if point to water
    size_t water_index = 0;
    bool water_picked =  m_picker.GetIntersectionModelId(water_index, GetRayForPicker(*(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs)));
    if (water_picked)
      {
      m_application.GetWorldUpdater().RemoveWater(water_index);
      _InitializePicker();
      return;
      }

    // check if point to ground
    TVector3 point;
    bool ground_picked = GetPickedPointInWorldCoords(point, *(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs), m_application.GetPicker());
    if (ground_picked)
      {
      m_application.GetWorldUpdater().DestroyObject(TVector2(point.X(), point.Y()));
      return;
      }
    }

  }

void DestroyObjectsEventHandler::MouseMove(const OIS::MouseEvent& evt)
  {
  // check if point to water
  size_t water_index = 0;
  bool water_picked =  m_picker.GetIntersectionModelId(water_index, GetRayForPicker(*(m_application.GetOgre().GetViewport()), TVector2(evt.state.X.abs, evt.state.Y.abs)));

  if (water_picked)
    {
    bool same_water_highlighting = water_index != m_water_index;
    if (!m_water_index.is_initialized() || same_water_highlighting)
      {
      mp_highlighted_model = m_application.GetWorld().GetGround()->GetWaterModels()[water_index]->GetModel().Clone();
      m_water_index = water_index;
      }
    else
      {
      return;
      }
    }
  else
    {
    m_water_index.reset();
    mp_highlighted_model = GetHighlightedModel(m_application, TVector2(evt.state.X.abs, evt.state.Y.abs));
    }

  if (mp_highlighted_model == nullptr)
    return;

  const double height_diff = 0.5;

  mp_highlighted_model->SetMaterialName("Highlighting/Red");
  std::string base_type = "HIGHLIGHTED_AREA_DELETE";
  mp_render_triangles.reset();  // Should delete old renderer first
  mp_render_triangles.reset(new Model3DRenderer(*mp_highlighted_model.get(), m_application.GetOgre().GetRoot()->getSceneManager("MonkeySceneManager"),
    m_application.GetOgre().GetRoot()->getMeshManager(), base_type + "_NAME", base_type, TVector3(0,height_diff,0), true));
  }

void DestroyObjectsEventHandler::MouseUp(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {

  }

void DestroyObjectsEventHandler::_InitializePicker()
  {
  m_picker = Picker();

  auto water_models = m_application.GetWorld().GetGround()->GetWaterModels();
  for (size_t i = 0; i < water_models.size(); ++i)
    m_picker.AddObject(&water_models[i]->GetModel(), TVector3(0.0, 0.0, 0.0));
  }
